#!/usr/bin/perl
## -*- mode: perl; indent-tabs-mode: nil; perl-indent-level: 4 -*-
## vim: autoindent tabstop=4 shiftwidth=4 expandtab softtabstop=4 filetype=perl

use strict;
use warnings;
use JSON::XS;
use Data::Dumper;
use Getopt::Long;
use File::Basename;

BEGIN {
    if (!(exists $ENV{'TOOLBOX_HOME'} && -d "$ENV{'TOOLBOX_HOME'}/perl")) {
    print "This script requires libraries that are provided by the toolbox project.\n";
    print "Toolbox can be acquired from https://github.com/perftool-incubator/toolbox and\n";
    print "then use 'export TOOLBOX_HOME=/path/to/toolbox' so that it can be located.\n";
    exit 1;
    }
}
use lib "$ENV{'TOOLBOX_HOME'}/perl";
use toolbox::json;
use toolbox::metrics;

my $script = "trafficgen-post-process";
my $script_name = basename($0);

my @trial_metrics = (
    { 'key' => 'flubberbubbles',
      'class' => 'pass/fail',
      'type' => 'trial-result',
      'name_format' => '',
      'altkey' => 'result',
      'altvalue' => 'status' }
    );

my @trial_stats_metrics = (
    { 'key' => 'global',
      'field' => 'runtime',
      'class' => 'count',
      'type' => 'trial-runtime-in-seconds',
      'name_format' => '' }
    );

my @trial_stats_device_metrics = (
    { 'key' => 'rx',
      'field' => 'rx_latency_maximum',
      'class' => 'count',
      'type' => 'max-roundtrip-usec',
      'name_format' => '%port_pair%-%rx_port%' },

    { 'key' => 'rx',
      'field' => 'rx_latency_average',
      'class' => 'count',
      'type' => 'mean-roundtrip-usec',
      'name_format' => '%port_pair%-%rx_port%' },

    { 'key' => 'tx',
      'field' => 'tx_l2_bps',
      'class' => 'throughput',
      'type' => 'l2-tx-bps',
      'name_format' => '%port_pair%-%tx_port%' },
    
    { 'key' => 'tx',
      'field' => 'tx_l1_bps',
      'class' => 'throughput',
      'type' => 'l1-tx-bps',
      'name_format' => '%port_pair%-%tx_port%' },

    { 'key' => 'rx',
      'field' => 'rx_l2_bps',
      'class' => 'throughput',
      'type' => 'l2-rx-bps',
      'name_format' => '%port_pair%-%rx_port%' },
    
    { 'key' => 'rx',
      'field' => 'rx_l1_bps',
      'class' => 'throughput',
      'type' => 'l1-rx-bps',
      'name_format' => '%port_pair%-%rx_port%' },

    { 'key' => 'rx',
      'field' => 'rx_pps',
      'class' => 'throughput',
      'type' => 'rx-pps',
      'name_format' => '%port_pair%-%rx_port%' },

    { 'key' => 'tx',
      'field' => 'tx_pps',
      'class' => 'throughput',
      'type' => 'tx-pps',
      'name_format' => '%port_pair%-%tx_port%' },

    { 'key' => 'rx',
      'field' => 'rx_lost_pps',
      'class' => 'throughput',
      'type' => 'lost-rx-pps',
      'name_format' => '%port_pair%-%rx_port%' },
    );

my @trial_profiler_metrics = (
    { 'key' => '',
      'subkey' => '',
      'field' => '',
      'class' => '',
      'type' => '',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'rx',
      'field' => 'pps',
      'class' => 'throughput',
      'type' => 'rx-pps',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'tx',
      'field' => 'pps',
      'class' => 'throughput',
      'type' => 'tx-pps',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'rx',
      'field' => 'bps',
      'class' => 'througput',
      'type' => 'rx-bps',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'tx',
      'field' => 'bps',
      'class' => 'throughput',
      'type' => 'tx-bps',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'rx',
      'field' => 'drop_bps',
      'class' => 'throughput',
      'type' => 'dropped-rx-bps',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'misc',
      'field' => 'cpu_util',
      'class' => 'count',
      'type' => 'tx-cores-cpu-util',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'rx',
      'field' => 'cpu_util',
      'class' => 'count',
      'type' => 'rx-core-cpu-util',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'misc',
      'field' => 'bw_per_core',
      'class' => 'throughput',
      'type' => 'per-core-Gbps',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 0 },

    { 'key' => 'global',
      'subkey' => 'misc',
      'field' => 'queue_full',
      'class' => 'count',
      'type' => 'queue-full-events',
      'name_format' => '',
      'extra_field' => '',
      'cumulative' => 1 },

    { 'key' => 'ports',
      'subkey' => 'rx',
      'field' => 'pps',
      'class' => 'throughput',
      'type' => 'rx-pps',
      'name_format' => '%rx_port%',
      'extra_field' => 'rx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'tx',
      'field' => 'pps',
      'class' => 'throughput',
      'type' => 'tx-pps',
      'name_format' => '%tx_port%',
      'extra_field' => 'tx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'rx',
      'field' => 'bps_l1',
      'class' => 'throughput',
      'type' => 'rx-l1-bps',
      'name_format' => '%rx_port%',
      'extra_field' => 'rx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'tx',
      'field' => 'bps_l1',
      'class' => 'throughput',
      'type' => 'tx-l1-bps',
      'name_format' => '%tx_port%',
      'extra_field' => 'tx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'rx',
      'field' => 'bps',
      'class' => 'throughput',
      'type' => 'rx-l2-bps',
      'name_format' => '%rx_port%',
      'extra_field' => 'rx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'tx',
      'field' => 'bps',
      'class' => 'throughput',
      'type' => 'tx-l2-bps',
      'name_format' => '%tx_port%',
      'extra_field' => 'tx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'rx',
      'field' => 'util',
      'class' => 'count',
      'type' => 'rx-port-util',
      'name_format' => '%rx_port%',
      'extra_field' => 'rx_port',
      'cumulative' => 0 },

    { 'key' => 'ports',
      'subkey' => 'tx',
      'field' => 'util',
      'class' => 'count',
      'type' => 'tx-port-util',
      'name_format' => '%tx_port%',
      'extra_field' => 'tx_port',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'average',
      'class' => 'count',
      'type' => 'mean-round-trip-usec',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'total_max',
      'class' => 'count',
      'type' => 'max-round-trip-usec',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'total_min',
      'class' => 'count',
      'type' => 'min-round-trip-usec',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'duplicate',
      'class' => 'count',
      'type' => 'duplicate-latency-packets',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'dropped',
      'class' => 'count',
      'type' => 'dropped-latency-packets',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'out_of_order',
      'class' => 'count',
      'type' => 'out-of-order-latency-packets',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'seq_too_high',
      'class' => 'count',
      'type' => 'before-expected-latency-packets',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'latency',
      'field' => 'seq_too_low',
      'class' => 'count',
      'type' => 'after-expected-latency-packets',
      'name_format' => '%stream_id%',
      'extra_field' => 'stream_id',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'tx_pps',
      'field' => 'stream_id',
      'class' => 'throughput',
      'type' => 'tx-pps',
      'name_format' => '%tx_port%%stream_id%',
      'extra_field' => 'tx_port',
      'cumulative' => 0 },

    { 'key' => 'pgids',
      'subkey' => 'rx_pps',
      'field' => 'stream_id',
      'class' => 'throughput',
      'type' => 'rx-pps',
      'name_format' => '%rx_port%%stream_id%',
      'extra_field' => 'rx_port',
      'cumulative' => 0 },

    );

if ($ENV{'RS_CS_LABEL'} =~ /^(client|server)-(\d+)$/) {
    if ($1 eq "server") {
        exit 0;
    }
};

my $result_file = "binary-search.json";
if (! -f $result_file) {
    printf "Could not find file %s in directory %s\n", $result_file, `/bin/pwd`;
    exit 1;
}
my $bs_json_ref = get_json_file($result_file);
my %sample; # The primary structure in which this benchmark sample is documented
my @periods; # Each trial of binary-search is a period

my $measurement_trial_index = -1;
for (my $index=scalar(@{ $$bs_json_ref{'trials'} }) - 1; $index>=0; $index--) {
    my %trial = %{ $$bs_json_ref{'trials'}[$index] };

    if (($trial{'trial_params'}{'trial_mode'} eq 'validation') &&
	($trial{'result'} eq 'pass')) {
	$measurement_trial_index = $index;
	last;
    }
}

for (my $index=0; $index<scalar(@{ $$bs_json_ref{'trials'} }); $index++) {
    my %trial = %{ $$bs_json_ref{'trials'}[$index] };
    my $period_name = "trial-" . $trial{'trial'};
    if ($index == $measurement_trial_index) {
        # binary-search's "final-validation" gets the name "measurement" for this period
        $period_name = "measurement";
    }
    my %period = ('name' => $period_name);
    my $trial_end = int $trial{'stats'}{'trial_stop'};
    my $trial_begin = int $trial{'stats'}{'trial_start'};

    for my $trial_metric ( @trial_metrics ) {
        my $metric_type = $$trial_metric{'type'};
        my $metric_value;
        my %metric_types;
        my %names = ();
        my %desc = ('class' => $$trial_metric{'class'},
                    'source' => 'trafficgen',
                    'type' => $metric_type);
        if (exists($$trial_metric{'altvalue'}) && exists($$trial_metric{'altkey'})) {
            $desc{'value-format'} = 'status';
            my %values = ('fail' => 0, 'pass' => 1);
            $metric_types{$metric_type}{'values'} = \%values;
            if ( $trial{$$trial_metric{'altkey'}} eq "pass" ) {
                $metric_value = 1;
            } else {
                $metric_value = 0;
            }
        } elsif (exists($trial{$$trial_metric{'key'}})) {
            $metric_value = $trial{$$trial_metric{'key'}};
        } else {
            $metric_value = 0.0;
        }
        my %s = ('end' => int $trial_end,
                 'begin' => int $trial_begin,
                 'value' =>  $metric_value);
        log_sample($period_name, \%desc, \%names, \%s);

        foreach my $dev_pair ( @{ $trial{'trial_params'}{'test_dev_pairs'} } ) {
            for my $trial_stats_device_metric ( @trial_stats_device_metrics ) {
                my $metric_type = $$trial_stats_device_metric{'type'};
                my $metric_value;
                my %metric_types;
                my %desc = ('class' => $$trial_stats_device_metric{'class'},
                            'source' => 'trafficgen',
                            'type' => $metric_type);
                my %s = ('end' => int $trial_end,
                        'begin' => int $trial_begin,
                        'value' =>  $trial{'stats'}{$$dev_pair{$$trial_stats_device_metric{'key'}}}{$$trial_stats_device_metric{'field'}});
                my %names = ('tx_port' => $$dev_pair{'tx'}, 'rx_port' => $$dev_pair{'rx'}, 'port_pair' => $$dev_pair{'dev_pair'});
                log_sample($period_name, \%desc, \%names, \%s);
            }
        }
        # Add other metric_types here
    }
    my $metric_data_name = finish_samples(); # completes samples for this trial (period) with file name metric-data-$period_name.json.xz
    my @metric_files = ( $metric_data_name );
    $period{'metric-files'} = \@metric_files;
    push(@periods, \%period);
}

$sample{'rickshaw-bench-metric'}{'schema'}{'version'} = "2021.04.12";
$sample{'periods'} = \@periods;
$sample{'primary-period'} = 'measurement';
$sample{'primary-metric'} = 'rx-pps';
my $coder = JSON::XS->new;
open(JSON_FH, ">post-process-data.json") ||
    die("Could not open file post-process-data.json for writing\n");
print JSON_FH $coder->encode(\%sample);
close JSON_FH;
